package utils

import (
	"strconv"
	"strings"
	"sync"
	"time"

	beego_context "github.com/beego/beego/v2/server/web/context"

	"github.com/dgrijalva/jwt-go"
)

// CurrentUser 当前用户信息存储键
const CurrentUser = "currentUser"

// UserInfo 简化的用户信息结构
type UserInfo struct {
	SN   string `json:"sn"`
	Name string `json:"name"`
	Role int8   `json:"role"`
}

// AuthMiddleware JWT认证中间件
func AuthMiddleware(ctx *beego_context.Context) {
	// 处理OPTIONS预检请求
	if ctx.Request.Method == "OPTIONS" {
		return
	}

	startTime := time.Now()

	// 记录请求日志
	defer func() {
		duration := time.Since(startTime)
		LogRequest(
			ctx.Request.Method,
			ctx.Request.URL.Path,
			ctx.Request.UserAgent(),
			ctx.Input.IP(),
			ctx.ResponseWriter.Status,
			duration,
		)
	}()

	// 白名单路径检查
	if isWhitelistPath(ctx.Request.URL.Path) {
		return
	}

	// 提取并验证JWT token
	user, err := extractAndValidateToken(ctx)
	if err != nil {
		respondWithError(ctx, ErrUnauthorized, err.Error())
		return
	}

	// 存储用户信息到上下文
	ctx.Input.SetData(CurrentUser, user)
}

// isWhitelistPath 检查是否为白名单路径
func isWhitelistPath(path string) bool {
	// 需要前缀匹配的路径
	prefixPaths := []string{
		"/api/records/clearchoujiang",
		"/api/user/login",
		"/api/user/register",
		"/swagger",
		"/upload",
		"/health",
	}

	// 检查前缀匹配
	for _, prefix := range prefixPaths {
		if strings.HasPrefix(strings.ToLower(path), prefix) {
			return true
		}
	}

	return false
}

// extractAndValidateToken 提取并验证JWT token
func extractAndValidateToken(ctx *beego_context.Context) (*UserInfo, error) {
	authHeader := ctx.Input.Header("Authorization")
	if authHeader == "" {
		return nil, NewError(CodeUnauthorized, "缺少Authorization头")
	}

	// 支持 "Bearer <token>" 格式
	tokenString := authHeader
	if strings.HasPrefix(authHeader, "Bearer ") {
		tokenString = strings.TrimPrefix(authHeader, "Bearer ")
	}

	// 解析JWT token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, NewError(CodeUnauthorized, "无效的签名方法")
		}
		return []byte(GetJWTSecret()), nil
	})

	if err != nil {
		return nil, NewError(CodeUnauthorized, "token解析失败: "+err.Error())
	}

	if !token.Valid {
		return nil, NewError(CodeUnauthorized, "无效的token")
	}

	// 提取用户信息
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return nil, NewError(CodeUnauthorized, "无效的token claims")
	}

	// 检查token是否过期
	if exp, ok := claims["exp"].(float64); ok {
		if int64(exp) <= time.Now().Unix() {
			return nil, NewError(CodeUnauthorized, "token已过期")
		}
	}

	// 构建用户信息
	user := &UserInfo{}

	if sn, ok := claims["sn"].(string); ok {
		user.SN = sn
	} else {
		return nil, NewError(CodeUnauthorized, "token中缺少用户学号")
	}

	if name, ok := claims["name"].(string); ok {
		user.Name = name
	}

	if role, ok := claims["r"].(float64); ok {
		user.Role = int8(role)
	}

	return user, nil
}

// respondWithError 统一错误响应
func respondWithError(ctx *beego_context.Context, err *AppError, detail string) {
	if detail != "" {
		err = NewError(err.Code, err.Message, detail)
	}

	response := ErrorResponse(err)
	ctx.ResponseWriter.WriteHeader(GetHTTPStatusCode(err.Code))
	ctx.Output.JSON(response, false, false)
}

// RateLimiter 简单的内存限流器
type RateLimiter struct {
	mu       sync.Mutex
	clients  map[string]*clientLimiter
	requests int
	window   time.Duration
}

type clientLimiter struct {
	count    int
	lastSeen time.Time
}

var globalRateLimiter *RateLimiter

// InitRateLimiter 初始化限流器
func InitRateLimiter() {
	if !AppConfig.RateLimitEnabled {
		return
	}

	globalRateLimiter = &RateLimiter{
		clients:  make(map[string]*clientLimiter),
		requests: AppConfig.RateLimitRequests,
		window:   time.Duration(AppConfig.RateLimitWindow) * time.Second,
	}

	// 定期清理过期客户端
	go func() {
		ticker := time.NewTicker(time.Minute)
		defer ticker.Stop()

		for range ticker.C {
			globalRateLimiter.cleanup()
		}
	}()
}

// cleanup 清理过期的客户端记录
func (rl *RateLimiter) cleanup() {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	now := time.Now()
	for ip, client := range rl.clients {
		if now.Sub(client.lastSeen) > rl.window {
			delete(rl.clients, ip)
		}
	}
}

// isAllowed 检查是否允许请求
func (rl *RateLimiter) isAllowed(clientIP string) bool {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	now := time.Now()
	client, exists := rl.clients[clientIP]

	if !exists {
		rl.clients[clientIP] = &clientLimiter{
			count:    1,
			lastSeen: now,
		}
		return true
	}

	// 如果超过时间窗口，重置计数
	if now.Sub(client.lastSeen) > rl.window {
		client.count = 1
		client.lastSeen = now
		return true
	}

	// 检查是否超过限制
	if client.count >= rl.requests {
		return false
	}

	client.count++
	client.lastSeen = now
	return true
}

// RateLimitMiddleware 限流中间件
func RateLimitMiddleware(ctx *beego_context.Context) {
	if globalRateLimiter == nil {
		return
	}

	clientIP := ctx.Input.IP()
	if !globalRateLimiter.isAllowed(clientIP) {
		ctx.ResponseWriter.Header().Set("X-RateLimit-Limit", strconv.Itoa(globalRateLimiter.requests))
		ctx.ResponseWriter.Header().Set("X-RateLimit-Window", strconv.Itoa(int(globalRateLimiter.window.Seconds())))

		response := ErrorResponseWithMessage(
			NewError(429, "请求过于频繁"),
			"请求频率超过限制，请稍后再试",
		)
		ctx.ResponseWriter.WriteHeader(429)
		ctx.Output.JSON(response, false, false)
		return
	}
}

// SecurityHeadersMiddleware 安全头中间件
func SecurityHeadersMiddleware(ctx *beego_context.Context) {
	headers := map[string]string{
		"X-Content-Type-Options":    "nosniff",
		"X-Frame-Options":           "DENY",
		"X-XSS-Protection":          "1; mode=block",
		"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
		"Content-Security-Policy":   "default-src 'self'",
		"Referrer-Policy":           "strict-origin-when-cross-origin",
	}

	for key, value := range headers {
		ctx.ResponseWriter.Header().Set(key, value)
	}
}

// GetCurrentUser 从上下文获取当前用户
func GetCurrentUser(ctx *beego_context.Context) (*UserInfo, error) {
	userData := ctx.Input.GetData(CurrentUser)
	if userData == nil {
		return nil, NewError(CodeUnauthorized, "用户未登录")
	}

	user, ok := userData.(*UserInfo)
	if !ok {
		return nil, NewError(CodeInternalError, "用户信息格式错误")
	}

	return user, nil
}

// RequireRole 角色权限检查
func RequireRole(ctx *beego_context.Context, requiredRole int8) error {
	user, err := GetCurrentUser(ctx)
	if err != nil {
		return err
	}

	if user.Role < requiredRole {
		return NewError(CodePermissionDenied, "权限不足")
	}

	return nil
}
